import random
from pprint import pprint

print(11111111, random.randint(1, 10))


class ClassName:
    pass


class Turtle:  # Python中的类名约定以大写字母开头
    """关于类的一个简单例子"""
    # 属性
    color = 'green'
    weight = 10
    legs = 4
    shell = True
    mouth = '大嘴'

    # 方法
    def climb(self):
        print('我正在很努力的向前爬...')

    def run(self):
        print('我正在飞快的向前跑...')

    def bite(self):
        print('咬死你咬死你!!')

    def eat(self):
        print('有得吃，真满足...')

    def sleep(self):
        print('困了，睡了，晚安，zzz')

    def kiss(self):
        print('嘿嘿嘿,让我亲一口,mua')


tt = Turtle()

print(tt)  # <__main__.Turtle object at 0x000001B7CD6B1198>
print(tt.__class__)  # <class '__main__.Turtle'>
print(type(Turtle))  # <class 'type'>
print(tt.__class__.__name__)  # Turtle

tt.climb()
tt.run()
tt.eat()
tt.bite()
tt.kiss()

"""
1.继承：子类自动共享父类之间数据和方法的机制
"""


class MyList(list):
    pass


lst = MyList([1, 5, 2, 7, 8])
lst.append(9)
lst.sort()
print(lst)

"""
多态：不同对象对同一方法响应不同的行动
"""


class Animal:
    name = '动物'

    def run(self):
        raise AttributeError('子类必须实现这个方法 ')


class People(Animal):
    name = '人'

    def run(self):
        print('%s在跑' % self.name)


class Dog(Animal):
    name = '狗'

    def run(self):
        print('%s在跑' % self.name)


class Pig(Animal):
    name = '猪仔'

    def run(self):
        print('%s在跑' % self.name)


def func(animal):
    animal.run()


func(Pig())  # 猪仔在跑
func(People())  # 人在跑
func(Dog())  # 狗在跑

"""
2.self是什么？
相当于javascript的this  指针

类的方法与普通的函数只有一个特别的区别 —— 它们必须有一个额外的第一个参数名称（对应于该实例，即该对象本
身），按照惯例它的名称是 self 。在调用方法时，我们无需明确提供与参数 self 相对应的参数。
"""


class Ball:
    name = None

    def set_name(self, name):
        self.name = name

    def kick(self):
        print('我是%s,奶奶个腿,刚谁踢我了' % self.name)


a = Ball()
a.set_name('A球')
a.kick()  # 我是A球,奶奶个腿,刚谁踢我了

b = Ball()
b.set_name('B球')  # 我是B球,奶奶个腿,刚谁踢我了
b.kick()

"""
3.Python的魔法方法
据说，Python 的对象天生拥有一些神奇的方法，它们是面向对象的 Python 的一切...
它们是可以给你的类增加魔力的特殊方法...
如果你的对象实现了这些方法中的某一个，那么这个方法就会在特殊的情况下被 Python 所调用，而这一切都是自动发生
的...
类有一个名为 __init__(self[, param1, param2...]) 的魔法方法，该方法在类实例化时会自动调用。
"""


class BallInit:
    def __init__(self, name):
        self.name = name

    def kick(self):
        print('我是%s,奶奶个腿,刚谁踢我了' % self.name)


a = BallInit('A球')
a.kick()

"""
4.公有和私有
在 Python 中定义私有变量只需要在变量名或函数名前加上“__”两个下划线，那么这个函数或变量就会为私有的了。
"""


class JustCounter:
    __secretCount = 0
    publicCount = 0

    def count(self):
        self.__secretCount += 3
        self.publicCount += 1
        print(self.publicCount)


counter = JustCounter()
counter.count()
counter.count()

print(counter.publicCount)
print(counter._JustCounter__secretCount)


# 类定义
class People:
    # 定义基本属性
    name = ''
    age = 0
    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0

    # 定义构造方法
    def __init__(self, n, a, w):
        self.name = n
        self.age = a
        self.__weight = w

    def speak(self):
        print("%s 说: 我 %d 岁。" % (self.name, self.age))


"""
5.继承
BaseClassName （示例中的基类名）必须与派生类定义在一个作用域内。除了类，还可以用表达式，基类定义在另一个
模块中时这一点非常有用：
"""


# 单继承示例
class Student(People):
    grade = ''

    def __init__(self, n, a, w, g):
        # 调用父类的构函
        People.__init__(self, n, a, w)
        self.grade = g

    # 覆写父类的方法
    def speak(self):
        print("%s 说: 我 %d 岁了，我在读 %d 年级" % (self.name, self.age, self.grade))


s = Student('小马的程序人生', 10, 60, 3)
s.speak()


# 小马的程序人生 说: 我 10 岁了，我在读 3 年级


#
class Fish:
    def __init__(self):
        self.x = random.randint(0, 10)
        self.y = random.randint(0, 10)

    def move(self):
        self.x -= 1
        print("我的位置", self.x, self.y)


class GoldFish(Fish):  # 金鱼
    pass


class Carp(Fish):  # 鲤鱼
    pass


class Salmon(Fish):  # 三文鱼
    pass


class Shark(Fish):  # 鲨鱼
    def __init__(self):
        # 调用未绑定的父类方法 Fish.__init__(self)
        # 使用super函数 super().__init__()
        # Python
        # 虽然支持多继承的形式，但我们一般不使用多继承，因为容易引起混乱。
        super().__init__()
        # Fish.__init__(self)
        self.hungry = True

    def eat(self):
        if self.hungry:
            print("吃货的梦想就是天天有得吃！")
            self.hungry = False
        else:
            print("太撑了，吃不下了！")
            self.hungry = True


g = GoldFish()
g.move()  # 我的位置 9 4
s = Shark()
s.eat()  # 吃货的梦想就是天天有得吃！
s.move()
# AttributeError: 'Shark' object has no attribute 'x'


"""
# 6.类、类对象和实例对象
1.类对象：创建一个类，其实也是一个对象也在内存开辟了一块空间，称为类对象，类对象只有一个。
2.实例对象：就是通过实例化类创建的对象，称为实例对象，实例对象可以有多个。
3.类属性：类里面方法外面定义的变量称为类属性。类属性所属于类对象并且多个实例对象之间共享同一个类属性，说白了
就是类属性所有的通过该类实例化的对象都能共享。

# 类属性和实例属性区别
1. 类属性：类外面，可以通过 实例对象.类属性 和 类名.类属性 进行调用。类里面，通过 self.类属性
和 类名.类属性 进行调用。
2. 实例属性 ：类外面，可以通过 实例对象.实例属性 调用。类里面，通过 self.实例属性 调用。
3. 实例属性就相当于局部变量。出了这个类或者这个类的实例对象，就没有作用了。
4. 类属性就相当于类里面的全局变量，可以和这个类的所有实例对象共享。

****** 属性与方法名相同，属性会覆盖方法。
"""


# 创建类对象
class Test(object):
    class_attr = 100  # 类属性

    def __init__(self):
        self.sl_attr = 100  # 实例属性

    def func(self):
        print('类对象.类属性的值:', Test.class_attr)  # 调用类属性
        print('self.类属性的值', self.class_attr)  # 相当于把类属性 变成实例属性
        print('self.实例属性的值', self.sl_attr)  # 调用实例属性


a = Test()
a.func()

"""
7.什么是绑定？
Python 严格要求方法需要有实例才能被调用，这种限制其实就是 Python 所谓的绑定概念。
Python 对象的数据属性通常存储在名为 .__ dict__ 的字典中，我们可以直接访问 __dict__ ，或利用 Python 的内置函
数 vars() 获取 .__ dict__ 。
"""


class CC:
    def __init__(self):
        self.y = None
        self.x = None

    def set_xy(self, x, y):
        self.x = x
        self.y = y

    def print_xy(self):
        print(self.x, self.y)


dd = CC()
print(dd.__dict__)  # {'y': None, 'x': None}

pprint(CC.__dict__)

dd.set_xy(4, 5)
pprint(dd.__dict__)  # {'x': 4, 'y': 5}

pprint(vars(CC))
pprint(vars(dd))

"""
8.一些相关的内置函数（BIF）
1. issubclass(childClass, FatherClass) 方法用于判断参数 childClass 是否是类型参数 FatherClass 的子类。
    一个类被认为是其自身的子类。
    FatherClass 可以是类对象的元组，只要childClass是其中任何一个候选类的子类，则返回 True 。
    isinstance(object, classinfo) 方法用于判断一个对象是否是一个已知的类型，类似 type() 。
2. type() 不会认为子类是一种父类类型，不考虑继承关系。
3. isinstance() 会认为子类是一种父类类型，考虑继承关系。
4. 如果第一个参数不是对象，则永远返回 False 。
5. 如果第二个参数不是类或者由类对象组成的元组，会抛出一个 TypeError 异常。

"""


class A:
    pass


class B(A):
    pass


print(issubclass(A, B))  # False
print(issubclass(B, A))  # True
print(issubclass(A, object))  # True

"""
1.hasattr(object, name) 用于判断对象是否包含对应的属性。
2.getattr(object, name[, default]) 用于返回一个对象属性值。
3.setattr(object, name, value) 对应函数 getattr() ，用于设置属性值，该属性不一定是存在的。
4.delattr(object, name) 用于删除属性。
5.class property([fget[, fset[, fdel[, doc]]]]) 用于在新式类中返回属性值。
    a. fget -- 获取属性值的函数
    b. fset -- 设置属性值的函数
    c. fdel -- 删除属性值函数
    d. doc -- 属性描述信息
"""


class C(object):
    def __init__(self):
        self.__x = None

    def getx(self):
        return self.__x

    def setx(self, value):
        self.__x = value

    def del_x(self):
        del self.__x

    x = property(getx, setx, del_x, "I'm the 'x' property.")


cc = C()
cc.x = 2
pprint(C.__dict__)  # 2
pprint(cc.x)  # 2

"""
9.魔法方法
魔法方法总是被双下划线包围，例如 __init__ 。
魔法方法是面向对象的 Python 的一切，如果你不知道魔法方法，说明你还没能意识到面向对象的 Python 的强大。
魔法方法的“魔力”体现在它们总能够在适当的时候被自动调用。
魔法方法的第一个参数应为 cls （类方法） 或者 self （实例方法）。
1. cls ：代表一个类的名称
2. self ：代表一个实例对象的名称
"""

"""
基本的魔法方法1  __init__(self[, ...])
1. 构造器，当一个实例被创建的时候调用的初始化方法
"""

"""
基本的魔法方法2 __new__(cls[, ...])
1. __new__ 是在一个对象实例化的时候所调用的第一个方法，在调用 __init__ 初始化前，先调用 __new__ 。
2. __new__ 至少要有一个参数 cls ，代表要实例化的类，此参数在实例化时由 Python 解释器自动提供，后面的参数直
接传递给 __init__ 。
3. __new__ 对当前类进行了实例化，并将实例返回，传给 __init__ 的 self 。但是，执行了 __new__ ，并不一定会
进入 __init__ ，只有 __new__ 返回了，当前类 cls 的实例，当前类的 __init__ 才会进入

 __new__ 方法主要是当你继承一些不可变的 class 时（比如 int, str, tuple ）， 提供给你一个自定义这些类的实
例化过程的途径。

"""

"""
基本的魔法方法3  __del__(self)
析构器，当一个对象将要被系统回收之时调用的方法。
"""

"""
基本的魔法方法4
__str__(self) :
1. 当你打印一个对象的时候，触发 __str__
2. 当你使用 %s 格式化的时候，触发 __str__
3. str 强转数据类型的时候，触发 __str__
__repr__(self):
1. repr 是 str 的备胎
2. 有 __str__ 的时候执行 __str__ ,没有实现 __str__ 的时候，执行 __repr__
3. repr(obj) 内置函数对应的结果是 __repr__ 的返回值
4. 当你使用 %r 格式化的时候 触发 __repr__
"""
